#pragma once
#include <unordered_map>

#include "protocol.hpp"

typedef long long ID_Type;

class ID_Generator{
  private:
    static pthread_mutex_t g_lock;
    static ID_Type id;
  public:
    static ID_Type GetID(){
      SmartLock sl(&g_lock);
      sl.Lock();
      return ++id;
    }

    static void GeneratorInit(){
      pthread_mutex_init(&g_lock, nullptr);
      id = 0;
    }
    
    static void GeneratorDestory(){
      pthread_mutex_destroy(&g_lock);
    }
};

ID_Type ID_Generator::id = 0;
pthread_mutex_t ID_Generator::g_lock;

const char* user_status[] = {"REQUIRE", "LOGIN", "WAITONLINE", "ONLINE", "OFFLINE", "INVISIBLE"};

enum UserStatus{
  REQUIRE = 0,
  LOGIN,
  WAITONLINE,
  ONLINE,
  OUTLINE,
  INVISIBLE
};

class UserInfo{
  public:
    UserInfo(const std::string& NickName = std::string(), const std::string& School = std::string(), const std::string& PassWord= std::string())
    :nick_name(NickName), school(School), pass_word(PassWord)
    ,u_id(ID_Generator::GetID()), u_status(REQUIRE){}

    void AlterNickName(const std::string& dest){
      nick_name = dest;
    }

    void AlterPassWord(const std::string& dest){
      pass_word = dest;
    }

    void AlterSchool(std::string dest){
      school = dest;
    }

    void SetStatus(UserStatus Status){
      u_status = Status;
    }

    UserStatus& GetStatus(){
      return u_status;
    }

    const std::string& GetNickName(){
      return nick_name;
    }

    const std::string& GetSchool(){
      return school;
    }

    const std::string& GetPassWord(){
      return pass_word;
    }

    const ID_Type& GetUserID(){
      return u_id;
    }
  private:
    std::string nick_name;
    std::string school;
    std::string pass_word;
    const ID_Type u_id;
    UserStatus u_status;
};

enum UserAttr{
  NICKNAME,
  SCHOOL,
  PASSWORD,
  MESSAGE
};

class UserManager{
  public:
    UserManager(){
      pthread_mutex_init(&op_lock, nullptr);
      ID_Generator::GeneratorInit();
    }

    ~UserManager(){
      ID_Generator::GeneratorDestory();
      pthread_mutex_destroy(&op_lock);
    }

    void SetStatus(ID_Type id, UserStatus _status){
      UserChart[id].SetStatus(_status);
    }

    void AlterAttr(ID_Type id, std::string dest, UserAttr attr){
      switch(attr){
        case NICKNAME:
          UserChart[id].AlterNickName(dest);
          break;
        case SCHOOL:
          UserChart[id].AlterSchool(dest);
          break;
        case PASSWORD:
          UserChart[id].AlterPassWord(dest);
          break;
      }
    }

    const std::string& GetAttr(ID_Type id, UserAttr attr){
      switch(attr){
        case NICKNAME:
          return UserChart[id].GetNickName();
        case SCHOOL:
          return UserChart[id].GetSchool();
        case PASSWORD:
          return UserChart[id].GetPassWord();
      }
    }

    //是否注册
    bool HaveRegister(const ID_Type& user_id){
      return UserChart.find(user_id) != UserChart.end();
    }

    //注册, 信息不完整则注册失败
    bool Register(UserInfo& user_info){
      if(user_info.GetNickName().empty()
          || user_info.GetSchool().empty()
          || user_info.GetPassWord().empty()){
        return false;
      }
      user_info.SetStatus(REQUIRE);
      UserChart.insert(std::make_pair(user_info.GetUserID(), user_info));
      return true;
    }

    //登录, 密码错误登录失败
    bool LoginOn(const ID_Type& user_id, const std::string& cur_input){
      if(cur_input.compare(UserChart[user_id].GetPassWord()) == 0){
        UserChart[user_id].SetStatus(ONLINE);
        return true;
      }
      else{
        return false;
      }
    }
 
    void Online(const ID_Type& user_id, sockaddr_in& cli_addr){
      AddrChart[user_id] = cli_addr;
    }

    void LogOut(const ID_Type& user_id){
      bool ret = AddrChart.erase(user_id);
      if(ret == 0){
        LOG("%s:erase none", level[ERROR]);
      }
      UserChart[user_id].SetStatus(OUTLINE);
    }

    sockaddr_in& GetUserAddr(ID_Type u_id){
      return AddrChart[u_id];
    }

    const std::unordered_map<ID_Type, sockaddr_in>& GetOnlineUser(){
      return AddrChart;
    }

  private:
    pthread_mutex_t op_lock;
    std::unordered_map<ID_Type, UserInfo> UserChart;
    std::unordered_map<ID_Type, sockaddr_in> AddrChart;
};
